//------------------------------------------------------------------------------------------------------
//   _____     _______ ____  _     ___  ____  ____    _____ _   _  ____ ___ _   _ _____   ______  ___ _ 
//  / _ \ \   / / ____|  _ \| |   / _ \|  _ \|  _ \  | ____| \ | |/ ___|_ _| \ | | ____| |  _ \ \/ / / |
// | | | \ \ / /|  _| | |_) | |  | | | | |_) | | | | |  _| |  \| | |  _ | ||  \| |  _|   | | | \  /| | |
// | |_| |\ V / | |___|  _ <| |__| |_| |  _ <| |_| | | |___| |\  | |_| || || |\  | |___  | |_| /  \| | |
//  \___/  \_/  |_____|_| \_\_____\___/|_| \_\____/  |_____|_| \_|\____|___|_| \_|_____| |____/_/\_\_|_|
//
// Overlord Engine v1.121
// Copyright Overlord Thomas Goussaert & Overlord Brecht Kets
// http://www.digitalartsandentertainment.com/
//------------------------------------------------------------------------------------------------------
//Precompiled Header [ALWAYS ON TOP IN CPP]
#include "stdafx.h"

#include "ParticleEmitterComponent.h"
#include "../Helpers/EffectHelper.h"
#include "../Content/ContentManager.h"
#include "../Content/TextureDataLoader.h"
#include "../Graphics/Particle.h"
#include "../Components/TransformComponent.h"
#include "../Diagnostics/Logger.h"


ParticleEmitterComponent::ParticleEmitterComponent(const wstring& assetFile, int particleCount):
	m_pEffect(nullptr),
	m_pVertexBuffer(nullptr),
	m_pInputLayout(nullptr),
	m_pInputLayoutSize(0),
	m_ParticleCount(particleCount),
	m_ActiveParticles(0),
	m_LastParticleInit(0.0f),
	m_Settings(ParticleEmitterSettings()),
	m_AssetFile(assetFile),
	m_pParticleTexture(nullptr)
{
	//See Lab10_2
	for (int i = 0; i < m_ParticleCount; ++i)
	{
		m_Particles.push_back( new Particle( m_Settings ) );
	}
}


ParticleEmitterComponent::~ParticleEmitterComponent(void)
{
	//See Lab10_2
	for (int i = 0; i < m_ParticleCount; ++i)
	{
		delete m_Particles[i];
		m_Particles.at(i) = nullptr;
	}
	m_Particles.clear();
	m_pInputLayout->Release();
	m_pVertexBuffer->Release();
}

void ParticleEmitterComponent::Initialize(const GameContext& gameContext)
{
	//See Lab10_2
	LoadEffect(gameContext);
	CreateVertexBuffer(gameContext);
	m_pParticleTexture = ContentManager::Load<TextureData>(m_AssetFile);
}

void ParticleEmitterComponent::LoadEffect(const GameContext& gameContext)
{
	//See Lab10_2
		
	m_pEffect = ContentManager::Load<ID3DX11Effect>(L"Resources/Effects/ParticleRenderer.fx");

	m_pDefaultTechnique = m_pEffect->GetTechniqueByIndex(0);

	m_pWvpVariable = m_pEffect->GetVariableByName("gWvp")->AsMatrix();

	m_pViewInverseVariable = m_pEffect->GetVariableByName("gViewInverse")->AsMatrix();

	m_pTextureVariable = m_pEffect->GetVariableByName("particleTexture")->AsShaderResource();
	EffectHelper::BuildInputLayout(gameContext.pDevice, m_pDefaultTechnique, &m_pInputLayout, m_pInputLayoutSize);

}

void ParticleEmitterComponent::CreateVertexBuffer(const GameContext& gameContext)
{
	//See Lab10_2

	if (m_pVertexBuffer)
		SafeRelease(m_pVertexBuffer);

	//Vertexbuffer
	D3D11_BUFFER_DESC buffDesc;
	buffDesc.Usage = D3D11_USAGE_DYNAMIC;
	buffDesc.ByteWidth = sizeof(ParticleVertex) * m_Particles.size();
	buffDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	buffDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	buffDesc.MiscFlags = 0;

	gameContext.pDevice->CreateBuffer(&buffDesc, NULL, &m_pVertexBuffer);	
}

void ParticleEmitterComponent::Update(const GameContext& gameContext)
{
	//See Lab10_2
	float particleInterval = 0;

	particleInterval = ( m_Settings.MinEnergy + m_Settings.MaxEnergy ) / 2 / m_Particles.size();
	m_LastParticleInit += gameContext.pGameTime->GetElapsed(); 

	m_ActiveParticles = 0;

	//BUFFER MAPPING CODE
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	gameContext.pDeviceContext->Map(m_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);

	//m_pVertexBuffer
	ParticleVertex* pBuffer = (ParticleVertex*) mappedResource.pData;

	for (UINT i = 0; i < m_Particles.size(); ++i)
	{
		m_Particles[i]->Update(gameContext);
		if (m_Particles[i]->IsActive())
		{
			pBuffer[m_ActiveParticles] = m_Particles[i]->GetVertexInfo();
			++m_ActiveParticles;
		}
		else if( m_LastParticleInit >= particleInterval )
		{
			m_Particles[i]->Init( GetTransform()->GetWorldPosition() );
			pBuffer[m_ActiveParticles] = m_Particles[i]->GetVertexInfo();
			++m_ActiveParticles;
			m_LastParticleInit = 0;
		}
	}
	gameContext.pDeviceContext->Unmap(m_pVertexBuffer, 0);
}

void ParticleEmitterComponent::Draw(const GameContext& gameContext)
{
	UNREFERENCED_PARAMETER(gameContext);
}

void ParticleEmitterComponent::PostDraw(const GameContext& gameContext)
{
	//See Lab10_2
	//1
	auto world = XMLoadFloat4x4( &GetTransform()->GetWorld() );
	auto view = XMLoadFloat4x4(&gameContext.pCamera->GetView());
	auto viewProjection = XMLoadFloat4x4(&gameContext.pCamera->GetViewProjection());
	m_pWvpVariable->SetMatrix(reinterpret_cast<float*>( &viewProjection ));
	//2
	XMMATRIX vieuwInverse = XMLoadFloat4x4( &gameContext.pCamera->GetViewInverse() );
	m_pViewInverseVariable->SetMatrix(reinterpret_cast<float*>(&vieuwInverse));
	//3
	m_pTextureVariable->SetResource(m_pParticleTexture->GetShaderResourceView());
	//4
	gameContext.pDeviceContext->IASetInputLayout(m_pInputLayout);
	//5
	gameContext.pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);
	//6
	UINT offset = 0;
	UINT stride = sizeof(ParticleVertex);
	gameContext.pDeviceContext->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset);
	//7
	D3DX11_TECHNIQUE_DESC techDesc;
	m_pDefaultTechnique->GetDesc( &techDesc );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
		m_pDefaultTechnique->GetPassByIndex(p)->Apply(0, gameContext.pDeviceContext);
		gameContext.pDeviceContext->Draw(m_ActiveParticles,0); 
    }
	
}


